#!/usr/bin/env python 
# coding:utf-8
# @Time :10/11/18 15:10

import threading
import time
from Queue import Queue

from beanstalkc import SocketError, CommandFailed

from common.pybeanstalk import PyBeanstalk


class MqHandler(threading.Thread):
    MAX_PUT_MSG_BEANSTALK = 3
    MAX_QUEUE_SIZE = 100

    def __init__(self, beanstalk_conf, tube_name, max_beanstalk_block=100, log=None):
        threading.Thread.__init__(self)
        # 守护线程
        self.daemon = True

        self.log = log

        self.__max_beanstalk_block = max_beanstalk_block

        self.__beanstalk = PyBeanstalk(beanstalk_conf['host'], beanstalk_conf['port'])

        self.__is_running = True

        self.__tube = tube_name

        self.__queue = Queue()

        self.start()

    def __is_mq_blocked(self):

        while True:
            try:
                count = self.__beanstalk.get_tube_count(self.__tube)
                break

            except CommandFailed, (_, status, results):
                if status == "NOT_FOUND":
                    self.log.error("__is_mq_blocked 获取消息队列不存在, tube={}".format(self.__tube))
                    count = 0
                else:
                    self.log.error("获取消息队列异常： tube={}, status={}".format(self.__tube, status))
                    count = self.__max_beanstalk_block

                # 退出程序不再重试
                break

            # 若连接异常, 则进程重试
            except SocketError as e:
                self.log.warn("beanstalk 连接异常...")
                time.sleep(10)
                self.__beanstalk.reconnect()
                self.log.exception(e)

            # 其他意外异常，捕获退出
            except Exception as e:
                count = self.__max_beanstalk_block
                self.log.error("获取消息队列大小失败：")
                self.log.exception(e)
                break

        if count >= self.__max_beanstalk_block:
            return True
        return False

    def put_msg(self, body):

        # while True:
        if not self.__is_running:
            self.log.info("put_msg 当前 MqHandler 程序未启动...")
            return False

        if not isinstance(body, basestring):
            self.log.error("put_msg 将要发送的数据类型错误，body={}, body_type={}".format(body, type(body)))
            return False

        self.__queue.put_nowait(body)

        queue_size = self.__queue.qsize()
        if queue_size > self.MAX_QUEUE_SIZE:
            self.log.warning("put_msg 当前队列阻塞，队列中的消息数目为={}".format(queue_size))
            time.sleep(20)
            # continue

        self.log.info("put_msg 向队列发送消息成功...")
        return True

    def run(self):
        self.log.info("开始启动消息队列...")
        while self.__is_running:

            # 当前队列没有任务, 则跳过
            if self.__queue.qsize() <= 0:
                time.sleep(1)
                # self.log.info("当前队列没有数据...")
                continue

            if self.__is_mq_blocked():
                self.log.info("当前消息队列阻塞...")
                time.sleep(1)
                continue

            body = self.__queue.get(timeout=3)

            # 判断获得的数据是否合法
            if not isinstance(body, basestring):
                self.log.error("当前队列获得非合法数据, 数据类型是={}，数据是={}".format(type(body), body))
                time.sleep(1)
                continue

            try:
                self.__beanstalk.put(self.__tube, body)
                # self.log.info("向 __beanstalk 发送消息成功...")

            except Exception as e:
                self.log.warn("发送到消息队列异常: ")
                self.log.exception(e)
                time.sleep(5)
